"""
"""
import unittest as ut
import sys
import gc
from pprint import pformat
import tempfile
import os
from uuid import generate as uuid4
from time import sleep

import basicVdsTest as vdst
import vdsClient as vdsc
from storageConnectionTestUtils import eStorageType, disconnectFromStorage, connectToStorage, validateConnections, CONNECTIONS_OPTION_DEFAULT
from storageDomainTestUtils import eDomainClass, generateDomainTargets, destroyDomainTargets
from confUtils import Validate
from vdsClient import BLANK_UUID
from testUtils import cleanVdsm, retry
from agentUtils import connectAgent

sys.path.append("../../") # Vdsm root dir

import storage.task as task

class VerifyPersistTaskMetadata(vdst.BasicVDSTest):
    def testSave(self):
        t = task.Task(uuid4(), "Tasky", "Funky Tasks")
        t._done = lambda : True #Done throws exception because this is not a real task.
        srcFd, path = tempfile.mkstemp()
        task.Task._saveMetaFile(path, t, t.fields)
        lines = open(path, "r").readlines()
        os.unlink(path)
        for line in lines:
            field, value = line.split("=")
            field = field.strip()
            value = value.strip()
            self.assertEquals(str(getattr(t, field)), value)

    def testLoad(self):
        t = task.Task(uuid4(), "Tasky", "Funky Tasks")
        t._done = lambda : True #Done throws exception because this is not a real task.
        srcFd, path = tempfile.mkstemp()
        task.Task._saveMetaFile(path, t, t.fields)

        t = task.Task(uuid4(), "Tasky Jr.", "Don't step on the grass Sam")
        t._done = lambda : True #Done throws exception because this is not a real task.
        task.Task._loadMetaFile(path, t, t.fields)

        lines = open(path, "r").readlines()
        os.unlink(path)
        for line in lines:
            field, value = line.split("=")
            field = field.strip()
            value = value.strip()
            self.assertEquals(str(getattr(t, field)), value)

class TasksStayOutOfTrash(vdst.BasicVDSTest):
    def _dummyMethod(self):
        i = 4 + 5
        i += 1

    def _runTask(self):
        ctask = task.Task(id=None, name="Zarathustra")
        return ctask.prepare(self._dummyMethod)

    def test(self):
        gc.collect() #Make sure everything is cleared or trashed
        origTrashSize = len(gc.garbage)

        result = self._runTask()

        gc.collect() #Make sure everything is cleared or trashed
        currentTrashSize = len(gc.garbage)

        if currentTrashSize > origTrashSize:
            self.fail("Something was trashed. Probably a circular reference with Task.")

        if currentTrashSize < origTrashSize:
            raise Exception("Test error. Trash shrunk during test. This shouldn't happen.")

class RevertTaskOnDifferentHost(vdst.BasicVDSTest):
    #For convenience. I didn't put it in the base class because I didn't want to commit.
    mycfg = property(lambda self: self.cfg[self.__class__.__name__])
    @classmethod
    def getConfigTemplate(cls):
        return {cls.__name__ : {
                "VdsUri1" : {"default" : "http://server:54321"},
                "VdsUri2" : {"default" : "http://server:54321"},
                "Connections" : {"default" : CONNECTIONS_OPTION_DEFAULT, "validator" : validateConnections},
                "DomainClass1" : {"default" : "data", "validator" : eDomainClass.parse},
                "Domain1" : {"default" : "GUID, GUID", "validator" : Validate.list},
                "StorageType1": {"default" : "nfs", "validator" : eStorageType.parse},
                "HostID1" : {"default" : 7, "validator" : Validate.int},
                "HostID2" : {"default" : 7, "validator" : Validate.int},
                "Pool1Key" : {"default" : "NFSKEY"}
            }
        }

    def setUp(self):
        #Setup clients
        self.client1 = vdsc.VDSClient()
        self.client1.setURI(self.mycfg["VdsUri1"])

        self.client2 = vdsc.VDSClient()
        self.client2.setURI(self.mycfg["VdsUri2"])

        #Make sure env is clean
        resp = self.assertVdscOK(self.client1.getConnectedStoragePoolsList())
        self.assertTrue(len(resp['poollist']) == 0, "VDSM 1 is connected to a pool. Dirty env. Bailing out.")

        resp = self.assertVdscOK(self.client2.getConnectedStoragePoolsList())
        self.assertTrue(len(resp['poollist']) == 0, "VDSM 2 is connected to a pool. Dirty env. Bailing out.")

        #Handle storage configuration
        connectToStorage(self.client1, self.mycfg["Connections"])
        connectToStorage(self.client2, self.mycfg["Connections"])
        self.domainTarget1 = generateDomainTargets(self.client1, self.mycfg["Connections"], [self.mycfg["Domain1"]])[0]

        #Create the tmp domain
        self.sdUUID = vdsc.VDSClient.newUUID()
        self.assertVdscOK(
            self.client1.createStorageDomain(self.mycfg["StorageType1"], self.sdUUID,
                "Storage Domain 1", self.domainTarget1, self.mycfg["DomainClass1"]
            )
        )

        #Create tmp pool
        self.spUUID = vdsc.VDSClient.newUUID()
        self.assertVdscOK(
            self.client1.createStoragePool(self.spUUID, "Swimming Pool", self.sdUUID, [self.sdUUID], 1)
            )

        self.assertVdscOK(
            self.client1.connectStoragePool(self.spUUID, self.mycfg["HostID1"], self.mycfg["Pool1Key"])
            )

        self.imgUUID = vdsc.VDSClient.newUUID()
        self.volUUID = vdsc.VDSClient.newUUID()

    def test(self):
        #Start SPM on client 1
        taskid = self.assertVdscOK(self.client1.spmStart(self.spUUID, -1, -1, -1, False))['uuid']
        self.assertVdscTaskOK(self.client1, taskid)

        self.assertVdscOK(
            self.client1.activateStorageDomain(self.sdUUID, self.spUUID)
        )

        #Create volume
        volsize = 5120

        taskID = self.assertVdscOK(
            self.client1.createVolume(self.sdUUID, self.spUUID, self.imgUUID, volsize,
            vdsc.COW_FORMAT, vdsc.SPARSE_VOL, "DATA", self.volUUID, "Nietzsche", blocked = False)
        )['uuid']
        self.assertVdscTaskOK(self.client1, taskID)

        #Stop SPM on client 1
        self.assertVdscOK(self.client1.spmStop(self.spUUID))

        #Refresh connection
        connectToStorage(self.client2, self.mycfg["Connections"])

        #Start SPM on client 2
        self.assertVdscOK(
            self.client2.connectStoragePool(self.spUUID, self.mycfg["HostID2"], self.mycfg["Pool1Key"], self.sdUUID, 1)
            )

        taskid = self.assertVdscOK(self.client2.spmStart(self.spUUID, -1, 0, -1, False))['uuid']
        self.assertVdscTaskOK(self.client2, taskid)

        #Revert Task
        self.assertVdscOK(self.client2.revertTask(taskID))

        #Make sure volume doesn't exist
        self.assertVdscFail(
                self.client2.getVolumeInfo(self.sdUUID, self.spUUID, self.imgUUID, self.volUUID))

    def tearDown(self):
        cleanVdsm(self.client1, self.mycfg["HostID1"], self.mycfg["Connections"])
        cleanVdsm(self.client2, self.mycfg["HostID2"], self.mycfg["Connections"])

class StopRecoveringTask(vdst.BasicVDSTest):
    #For convenience. I didn't put it in the base class because I didn't want to commit.
    mycfg = property(lambda self: self.cfg[self.__class__.__name__])
    @classmethod
    def getConfigTemplate(cls):
        return {cls.__name__ : {
                "VdsUri1" : {"default" : "http://server:54321"},
                "Connections" : {"default" : CONNECTIONS_OPTION_DEFAULT, "validator" : validateConnections},
                "DomainClass1" : {"default" : "data", "validator" : eDomainClass.parse},
                "Domain1" : {"default" : "GUID, GUID", "validator" : Validate.list},
                "StorageType1": {"default" : "nfs", "validator" : eStorageType.parse},
                "HostID1" : {"default" : 7, "validator" : Validate.int},
                "Pool1Key" : {"default" : "NFSKEY"}
            }
        }

    def setUp(self):
        #Setup clients
        self.client = vdsc.VDSClient()
        self.client.setURI(self.mycfg["VdsUri1"])
        self.agent = connectAgent(self.mycfg["VdsUri1"])

        #Make sure env is clean
        resp = self.assertVdscOK(self.client.getConnectedStoragePoolsList())
        self.assertTrue(len(resp['poollist']) == 0, "VDSM is connected to a pool. Dirty env. Bailing out.")

        #Handle storage configuration
        connectToStorage(self.client, self.mycfg["Connections"])
        self.domainTarget1 = generateDomainTargets(self.client, self.mycfg["Connections"], [self.mycfg["Domain1"]])[0]

        #Create the tmp domain
        self.sdUUID = vdsc.VDSClient.newUUID()
        self.assertVdscOK(
            self.client.createStorageDomain(self.mycfg["StorageType1"], self.sdUUID,
                "Storage Domain 1", self.domainTarget1, self.mycfg["DomainClass1"]
            )
        )

        #Create tmp pool
        self.spUUID = vdsc.VDSClient.newUUID()
        self.assertVdscOK(
            self.client.createStoragePool(self.spUUID, "Swimming Pool", self.sdUUID, [self.sdUUID], 1)
            )

        self.assertVdscOK(
            self.client.connectStoragePool(self.spUUID, self.mycfg["HostID1"], self.mycfg["Pool1Key"])
            )

        self.imgUUID = vdsc.VDSClient.newUUID()
        self.volUUID = vdsc.VDSClient.newUUID()

        #Start SPM on client
        taskid = self.assertVdscOK(self.client.spmStart(self.spUUID, -1, -1, -1, False))['uuid']
        self.assertVdscTaskOK(self.client, taskid)

        self.assertVdscOK(
            self.client.activateStorageDomain(self.sdUUID, self.spUUID)
        )

        #Create volume
        volsize = 500 * (2 ** 20)
        taskID = self.assertVdscOK(
            self.client.createVolume(self.sdUUID, self.spUUID, self.imgUUID, volsize,
            vdsc.COW_FORMAT, vdsc.SPARSE_VOL, "DATA", self.volUUID, "Nietzsche", blocked = False)
        )['uuid']
        self.assertVdscTaskOK(self.client, taskID)

    def test(self):
        taskID = self.assertVdscOK(
            self.client.deleteImage(self.sdUUID, self.spUUID, self.imgUUID, True, False, False)
        )['uuid']

        sleep(1)
        self.agent.crashVDSM()

        retry(lambda : self.assertVdscOK(self.client.list()), 20)

        print 'Reconnect storage'
        connectToStorage(self.client, self.mycfg["Connections"])
        self.assertVdscOK(
            self.client.connectStoragePool(self.spUUID, self.mycfg["HostID1"], self.mycfg["Pool1Key"])
            )

        print 'wait for spm to start'
        taskid = self.assertVdscOK(self.client.spmStart(self.spUUID, self.mycfg["HostID1"], 0, -1, True))['uuid']
        # If SPM takes too long to start this means it does things it shouldn't
        self.assertVdscTaskOK(self.client, taskid, 30)

        print 'stop delete task recover'
        self.assertVdscOK(self.client.stopTask(taskID))

        print 'stop wait for all to be done'
        try:
            self.assertVdscTaskFail(self.client, taskID, 60)
        except vdsc.TaskNotFinished:
            self.fail("Task is stuck")

    def tearDown(self):
        cleanVdsm(self.client, self.mycfg["HostID1"], self.mycfg["Connections"])

def suite():
    tests = {
        RevertTaskOnDifferentHost : ["test"],
        TasksStayOutOfTrash : ["test"],
        VerifyPersistTaskMetadata : ["testSave", "testLoad"],
        StopRecoveringTask : ["test"]
    }

    resSuite = ut.TestSuite()
    for testcase, methods in tests.iteritems():
        resSuite.addTests(map(testcase, methods))

    return resSuite

validateConfig = lambda cfg : vdst.validateSuiteConfig(suite(), cfg)
